package utils

import (
	"bytes"
	"math/big"
)

// ---------------base58编解码------------

/*
	base58算法和base64类似。base58的不同是使用了更短的字母表：为了避免一些利用字母相似性的攻击，
从字母表中移除了一些字母。也就是，没有这些符号：0(零)，O(大写的 o)，I(大写的i)，l(小写的 L)，
因为这几个字母看着很像。另外，也没有 + 和 / 符号。
*/

var b58Alphabet = []byte("123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz")

// 加密base58数据
func Base58Encode(input []byte) []byte {
	var result []byte
/*
	（1）func NewInt(x int64) *Int
	创建一个值为x的*Int。
	（2）func (z *Int) SetBytes(buf []byte) *Int
	将buf视为一个大端在前的无符号整数，将z设为该值，并返回z。
*/
	x := big.NewInt(0).SetBytes(input)

	base := big.NewInt(int64(len(b58Alphabet)))
	zero := big.NewInt(0)
	mod := &big.Int{}

	for x.Cmp(zero) != 0 {
/*
	func (z *Int) DivMod(x, y, m *Int) (*Int, *Int)
如果y != 0将z设为x/y，将m设为x%y并返回(z, m)；如果y == 0会panic。采用欧几里德除法（和Go不同）
DivMod方法实现了欧几里德带余除法：
q = x div y  满足
m = x - y*q  且 0 <= m < |q|
*/
		x.DivMod(x, base, mod)
		result = append(result, b58Alphabet[mod.Int64()])
	}

	ReverseBytes(result)
	for b := range input {
		if b == 0x00 {
			result = append([]byte{b58Alphabet[0]}, result...)
		} else {
			break
		}
	}

	return result
}

// 解密base58数据
func Base58Decode(input []byte) []byte {
	result := big.NewInt(0)
	zeroBytes := 0

	for b := range input {
		if b == 0x00 {
			zeroBytes++
		}
	}

	payload := input[zeroBytes:]
	for _, b := range payload {
	/*
		func IndexByte(s []byte, c byte) int
		字符c在s中第一次出现的位置，不存在则返回-1。
	*/
		charIndex := bytes.IndexByte(b58Alphabet, b)
	/*
		func (z *Int) Mul(x, y *Int) *Int
		将z设为x * y并返回z。
	*/
		result.Mul(result, big.NewInt(58))
	/*
		func (z *Int) Add(x, y *Int) *Int
		将z设为x + y并返回z。
	*/
		result.Add(result, big.NewInt(int64(charIndex)))
	}

	decoded := result.Bytes()
	/*
		func Repeat(b []byte, count int) []byte
		返回count个b串联形成的新的切片。
	*/
	decoded = append(bytes.Repeat([]byte{byte(0x00)}, zeroBytes), decoded...)

	return decoded
}
